#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>

#include <config.h>

#ifdef JPEG_SUPPORT

#include <systems.h>
#include <gapi.h>
#include <image_jpeg.h>
#include <image.h>


#undef DPRINTF
//#define JPEG_DEBUG
#ifdef JPEG_DEBUG
#define DPRINTF(str, args...)   fprintf(stderr, "DEBUG: " str, ##args)  /* debug output*/
#else
#define DPRINTF(str, ...)
#endif

/*
 * Include file for users of JPEG library.
 * You will need to have included system headers that define at least
 * the typedefs FILE and size_t before you can include jpeglib.h.
 * (stdio.h is sufficient on ANSI-conforming systems.)
 * You may also wish to include "jerror.h".
 */

#include <libjpeg/jpeglib.h>

/*
 * <setjmp.h> is used for the optional error recovery mechanism shown in
 * the second part of the example.
 */

#include <setjmp.h>


struct my_error_mgr {
    struct jpeg_error_mgr pub;	/* "public" fields */
    jmp_buf setjmp_buffer;	/* for return to caller */
};

typedef struct my_error_mgr * my_error_ptr;

/*
 * Here's the routine that will replace the standard error_exit method:
 */

void my_error_exit(j_common_ptr cinfo)
{
    /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
    my_error_ptr myerr = (my_error_ptr) cinfo->err;

    /* Always display the message. */
    /* We could postpone this until after returning, if we chose. */
    (*cinfo->err->output_message) (cinfo);

    /* Return control to the setjmp point */
    longjmp(myerr->setjmp_buffer, 1);
}

static void fskip(FILE *fp, int num_bytes)
{
    int i;
    for (i=0; i<num_bytes; i++)
        fgetc(fp);
}

JPEG *load_jpeg(u8 * filename)
{
    JPEG *image;
    u8 magic[5];
	int i, j;
    JSAMPARRAY scanarray[3];
    int h_samp[3],v_samp[3];
    int max_h_samp,max_v_samp;
    int temp_h, temp_v;
    JDIMENSION ncols[3];
    JDIMENSION nrows[3];
    jpeg_component_info *compptr;
    int buffer_height;
    int cp;
#if !defined(NIOS2_COLOR_TRANSFORM_SUPPORT)
    int r, g, b;
#endif
  
    /* This struct contains the JPEG decompression parameters and pointers to
     * working space (which is allocated as needed by the JPEG library).
     */
    struct jpeg_decompress_struct cinfo;
    /* We use our private extension JPEG error handler.
     * Note that this struct must live as long as the main JPEG parameter
     * struct, to avoid dangling-pointer problems.
     */
    struct my_error_mgr jerr;
    FILE * infile;        /* source file */

    int counter = 0;

    if ((infile = fopen(filename, "rb")) == NULL) {
        ERROR("Can't open %s\n", filename);
        return 0;
    }

    /* first determine if JPEG file since decoder will error if not */
    magic[0] = fgetc(infile);
    magic[1] = fgetc(infile);
    if (magic[0] != 0xFF || magic[1] != 0xD8) {
        fclose(infile);
        ERROR("%s is not a jpeg file. (magic error)\n", filename);
        DPRINTF("magic[0] = %x, magic[1] = %x\n", magic[0], magic[1]);
        return 0;    /* not JPEG image */
    }
    fskip(infile, 4);
    fgets(magic, 5, infile);
    if((strncmp(magic, "JFIF", 4) != 0 && strncmp(magic, "Exif", 4) != 0)) {
          fclose(infile);
        ERROR("%s is not a jpeg file.\n", filename);
        DPRINTF("magic = %s\n", magic);
        return 0;    /* not JPEG image */
    }
    fseek(infile, 0, SEEK_SET);
    /* OK! It's a JPEG file. Let's go decompressing. */
    /* Step 1: allocate and initialize JPEG decompression object */

    /* We set up the normal JPEG error routines, then override error_exit. */
    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;
    /* Establish the setjmp return context for my_error_exit to use. */
    if (setjmp(jerr.setjmp_buffer)) {
        /* If we get here, the JPEG code has signaled an error.
         * We need to clean up the JPEG object, close the input file, and return.
         */
        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        return 0;
    }
  
    /* Now we can initialize the JPEG decompression object. */
    jpeg_create_decompress(&cinfo);

    /* Step 2: specify data source (eg, a file) */

    jpeg_stdio_src(&cinfo, infile);

    /* Step 3: read file parameters with jpeg_read_header() */

    (void) jpeg_read_header(&cinfo, TRUE);
    /* We can ignore the return value from jpeg_read_header since
     *   (a) suspension is not possible with the stdio data source, and
     *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
     * See libjpeg.doc for more info.
     */

    /* Step 4: set parameters for decompression */
    cinfo.out_color_space = JCS_YCbCr;
    cinfo.dct_method = JDCT_IFAST;
    cinfo.raw_data_out = TRUE;
    cinfo.do_fancy_upsampling = TRUE;
    cinfo.do_block_smoothing = FALSE;
    
    /* In this example, we don't need to change any of the defaults set by
     * jpeg_read_header(), so we do nothing here.
     */

    /* Step 5: Start decompressor */
    (void) jpeg_start_decompress(&cinfo);
    /* We can ignore the return value since suspension is not possible
     * with the stdio data source.
     */
  
    /* Step 6: while (scan lines remain to be read) */
    /*           jpeg_read_scanlines(...); */
    image = (JPEG *)malloc(sizeof(JPEG));
    image->direct_write_to_fb = false;
    image->width = cinfo.output_width;
    image->height = cinfo.output_height;
    if(!image->direct_write_to_fb) {
        DPRINTF("JPEG Width: %d, Height: %d\n", cinfo.output_width, cinfo.output_height);
#if defined(CS_RGB565)
        image->data = (u8 *)malloc_debug(cinfo.output_width * cinfo.output_height * 2);
#elif defined(CS_RGB888)
        image->data = malloc_debug(cinfo.output_width * cinfo.output_height * 3);
#endif    
        if(image->data == NULL) {
            ERROR("Error allocating memory for file %s.\n", filename);
            return 0;
        }
        DPRINTF("JPEG address: %x\n", (u32)image->data);
    }
    else if(image->data == NULL) {
        DPRINTF("Start address in framebuffer of jpeg image is not set!\n");
        return 0;
    }    
  
  
    /* JSAMPLEs per row in output buffer  */
    /* collect component subsample values*/
    for(cp = 0, compptr = cinfo.comp_info; cp < cinfo.num_components;
        cp++, compptr++) {
        h_samp[cp] = compptr->h_samp_factor;
        v_samp[cp] = compptr->v_samp_factor;
    }
    /* calculate max subsample values*/
    temp_h = (h_samp[0]<h_samp[1]) ? h_samp[1] : h_samp[0];
    max_h_samp = (temp_h<h_samp[2]) ? h_samp[2]:temp_h;
    temp_v = (v_samp[0]<v_samp[1]) ? v_samp[1] : v_samp[0];
    max_v_samp = (temp_v<v_samp[2]) ? v_samp[2]:temp_v;
      
    buffer_height = cinfo.max_v_samp_factor * cinfo.min_DCT_scaled_size;
    for(cp = 0, compptr = cinfo.comp_info; cp < cinfo.num_components;
        cp++,compptr++) {
        ncols[cp] = (JDIMENSION)((cinfo.image_width*compptr->h_samp_factor)/
                                  max_h_samp);
    
        nrows[cp] = (JDIMENSION)((buffer_height*compptr->v_samp_factor)/
                                  max_v_samp);
    
        scanarray[cp] = (*cinfo.mem->alloc_sarray)
                        ((j_common_ptr) &cinfo, JPOOL_IMAGE, ncols[cp], nrows[cp]);
    
    }
      
    /* Here we use the library's state variable cinfo.output_scanline as the
     * loop counter, so that we don't have to keep track ourselves.
     */
    DPRINTF("Read scanline loop\n");
    while (cinfo.output_scanline < cinfo.output_height) {
        /* jpeg_read_scanlines expects an array of pointers to scanlines.
         * Here the array is only one element long, but you could ask for
         * more than one scanline at a time if that's more convenient.
         */
        //rowptr[0] = (JSAMPROW) &data[0];
        //counter += jpeg_read_scanlines(&cinfo, rowptr, (JDIMENSION) 1);
        counter += jpeg_read_raw_data(&cinfo, scanarray, buffer_height);

#if defined(CS_RGB565)
        /* Convert one row to RGB565 */
        for(i = 0; i < buffer_height; i++) {
             for(j = 0; j < cinfo.output_width; j++) {
#if defined(NIOS2_COLOR_TRANSFORM_SUPPORT)
                ((u16 *)(image->data))[(counter - buffer_height + i) * cinfo.output_width + j] = 
                    CT_YCbCr2RGB565((u8)scanarray[0][i][j] << 16 | (u8)scanarray[1][i][j] << 8 | (u8)scanarray[2][i][j]);
#else
                 r = YUV2RGB_R(scanarray[0][i][j], scanarray[1][i][j], scanarray[2][i][j]);
                 if(r < 0) r = 0; else if(r > 255) r = 255;
                 g = YUV2RGB_G(scanarray[0][i][j], scanarray[1][i][j], scanarray[2][i][j]);
                 if(g < 0) g = 0; else if(g > 255) g = 255;
                 b = YUV2RGB_B(scanarray[0][i][j], scanarray[1][i][j], scanarray[2][i][j]);
                 if(b < 0) b = 0; else if(b > 255) b = 255;
                 ((u16 *)(image->data))[(counter - 8 + i) * cinfo.output_width + j] = RGB2PIXEL565(r, g, b);
#endif
             }
        }

#elif defined(CS_RGB888)
# error "Not yet implemented!"
        //memcpy(&image->data[cinfo.output_scanline * w], data, row_stride);
#endif     
        /* Assume put_scanline_someplace wants a pointer and sample count. */
        //put_scanline_someplace(buffer[0], row_stride);
    }
 
    /* Step 7: Finish decompression */

    (void) jpeg_finish_decompress(&cinfo);
    /* We can ignore the return value since suspension is not possible
     * with the stdio data source.
     */

    /* Step 8: Release JPEG decompression object */

    /* This is an important step since it will release a good deal of memory. */
    jpeg_destroy_decompress(&cinfo);

    /* After finish_decompress, we can close the input file.
     * Here we postpone it until after no more JPEG errors are possible,
     * so as to simplify the setjmp error logic above.  (Actually, I don't
     * think that jpeg_destroy can do an error exit, but why assume anything...)
     */
    fclose(infile);

    /* At this point you may want to check to see whether any corrupt-data
     * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
     */

    /* And we're done! */
    
    return image;
}

#endif /* JPEG_SUPPORT */
